package qc.module.cms.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import qc.common.core.enums.CmsArticleStatusFlagEnum;
import qc.common.core.exception.QCPromptException;
import qc.common.core.utils.DateUtil;
import qc.module.cms.dto.article.ArticleDto;
import qc.module.cms.dto.statistics.*;
import qc.module.cms.entity.Article;
import qc.module.cms.mapper.ArticleMapper;
import qc.module.cms.repository.ArticleRepository;

import java.text.Collator;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @projectName: qcdp
 * @package: qc.module.cms.service
 * @className: StatisticsService
 * @date: 2023/7/31
 */
@Service
public class StatisticsService {
    private ArticleRepository repository;

    @Autowired
    public void setArticleRepository(ArticleRepository repository) {
        this.repository = repository;
    }


    /**
     * 统计指定时间段、指定栏目中发布文章情况，按作者和部门统计
     *
     * @param condition: 查询条件
     * @return ArticleStatisticsResultDto 查询结果
     * @author QcCheng Tech
     * @date 2023/8/1
     */
    public ArticleStatisticsResultDto query(ArticleStatisticsConditionDto condition) throws QCPromptException {
        ArticleStatisticsResultDto articleStatisticsResultDto = new ArticleStatisticsResultDto();

        List<Article> articles = queryArticles(condition, null);
        if (articles != null && articles.size() > 0x0) {
            // 统计每个部门的文章数量
            Map<String, Integer> departmentCountMap = new HashMap<>();
            for (Article article : articles) {
                String department = article.getIndept();
                if (StringUtils.isBlank(article.getIndept())) {
                    continue; // 如果部门为空，则跳过本次循环
                }
                departmentCountMap.put(department, departmentCountMap.getOrDefault(department, 0) + 1);
            }

            // 处理部门查询结果
            List<ArticleStatisticsDeptItemDto> departmentList = new ArrayList<>();
            for (Map.Entry<String, Integer> entry : departmentCountMap.entrySet()) {
                ArticleStatisticsDeptItemDto deptItem = new ArticleStatisticsDeptItemDto();
                deptItem.setName(entry.getKey());
                deptItem.setCount(entry.getValue());
                departmentList.add(deptItem);
            }
            articleStatisticsResultDto.setDepts(departmentList);

            // 统计每个作者的文章数量
            //2023-10-8，按作者统计修改为按inuser进行统计，每篇文章只有1个作者
            Map<String, Integer> authorCountMap = new HashMap<>();
            for (Article article : articles) {
                String author = article.getInuser();
                if (StringUtils.isBlank(author)) {
                    continue; // 如果作者为空，则跳过本次循环
                }
                authorCountMap.put(author, authorCountMap.getOrDefault(author, 0) + 1);
            }

            // 处理作者查询结果
            List<ArticleStatisticsAuthorItemDto> authorList = new ArrayList<>();
            for (Map.Entry<String, Integer> authorEntry : authorCountMap.entrySet()) {
                ArticleStatisticsAuthorItemDto authorItem = new ArticleStatisticsAuthorItemDto();
                authorItem.setName(authorEntry.getKey());
                authorItem.setCount(authorEntry.getValue());
                authorList.add(authorItem);
            }
            articleStatisticsResultDto.setAuthors(authorList);
        }

        return articleStatisticsResultDto;
    }

    /**
     * 指定作者或部门查询文章数量
     *
     * @param condition:      查询条件
     * @param isAssignAuthor: true指定作者，false指定部门
     * @return List<ArticleDto> 查询结果
     * @author QcCheng Tech
     * @date 2023/8/1
     */
    public List<ArticleDto> queryArticleCountByDeptOrAuthor(ArticleStatisticsConditionDto condition, boolean isAssignAuthor) throws QCPromptException {
        List<Article> articles = queryArticles(condition, isAssignAuthor);

        if (articles != null && articles.size() > 0x0) {
            return ArticleMapper.MAPPER.toDtoList(articles);
        }

        return null;
    }

    /***
     * 查询指定条件的文章列表，文章信息不中查询文章内容
     *
     * @param condition 查询条件：起止时间、栏目ID集合、作者、部门
     * @param isAssignAuthor 为null表示无作者或部门条件，为true表示根据指定作者查询，为false表示根据指定部门查询
     * @return java.util.List<qc.module.cms.entity.Article>
     * @author QuCheng Tech
     * @since 2023/10/8
     */
    public List<Article> queryArticles(ArticleStatisticsConditionDto condition, Boolean isAssignAuthor) throws QCPromptException {
        if (condition == null) throw new QCPromptException("查询条件不能为空");
        if (StringUtils.isBlank(condition.getBegintm())) throw new QCPromptException("起始时间不能为空！");
        if (StringUtils.isBlank(condition.getEndtm())) throw new QCPromptException("截止时间不能为空！");
        Date beginTime;
        Date endTime;
        try {
            // 解析起始时间和截止时间
            beginTime = DateUtil.parseDate(condition.getBegintm());
        } catch (Exception e) {
            throw new QCPromptException("开始时间解析出错");
        }

        try {
            // 解析起始时间和截止时间
            endTime = DateUtil.parseDate(condition.getEndtm());
        } catch (Exception e) {
            throw new QCPromptException("结束时间解析出错");
        }
        //判断是否为根据指定作者或指定部门查询
        if (isAssignAuthor != null) {
            if (isAssignAuthor.booleanValue() == true) {
                //指定作者查询
                if (StringUtils.isBlank(condition.getAuthor())) {
                    throw new QCPromptException("作者不能为空！");
                }
            } else {
                //指定部门查询
                if (StringUtils.isBlank(condition.getDept())) {
                    throw new QCPromptException("部门不能为空！");
                }
            }
        }

        LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>();

        // 选择需要查询的字段
        //指定作者和指定部门查询需要文章的标题等信息，不指定作者或部门的不需要标题信息（只需要文章ID、作者和部门）
        if (isAssignAuthor != null)
            //指定为查询不包含内容列和附件列
            wrapper.select(Article::getId, Article::getCid, Article::getTitle, Article::getCtype, Article::getKeywords, Article::getSource, Article::getAuthor, Article::getIntm, Article::getInuser, Article::getIndept, Article::getUptm, Article::getUpuser, Article::getFlag, Article::getTags, Article::getCount);
        else
            wrapper.select(Article::getId, Article::getInuser, Article::getIndept);

        // 查询条件指定状态标记为----已发布
        wrapper.eq(Article::getFlag, CmsArticleStatusFlagEnum.PUBLISH);

        // 在起始时间和截止时间之间进行查询
        //2023-10-7修改查询时间段以日期为最小单位，后台自动处理截止时间往后1天，条件为>=beignTime && <endTime
        wrapper.ge(Article::getIntm, beginTime);
        wrapper.lt(Article::getIntm, DateUtil.addDays(endTime, 1));

        // 匹配栏目,若不选择,查询全部
        if (CollectionUtils.isNotEmpty(condition.getIds()))
            wrapper.in(Article::getCid, condition.getIds());

        //判断是否为根据指定作者或指定部门查询
        if (isAssignAuthor != null) {
            if (isAssignAuthor.booleanValue() == true) {
                //指定作者查询
                wrapper.eq(Article::getInuser, condition.getAuthor());
            } else {
                //指定部门查询
                wrapper.eq(Article::getIndept, condition.getDept());
            }
        }
        //排序
        wrapper.orderByAsc(Article::getUptm, Article::getIntm);

        // 执行查询
        return repository.selectList(wrapper);
    }

    /**
     * 统计指定时间段、指定栏目中发布文章情况，按发布人统计结果和按部门统计结果；
     * 发布人统计包含文章列表和排名前5（其他的合并显示为其他）；
     * 部门统计按部门进行分类，每个部门中显示发文的发布人名称和文章数量；
     *
     * @param condition: 查询条件
     * @return ArticleMultiStatisticsResultDto 查询结果
     * @author QcCheng Tech
     * @date 2023/11/8
     */
    public ArticleMultiStatisticsResultDto statisticsByInUserAndDept(ArticleStatisticsConditionDto condition) throws QCPromptException {
        // 1.查询指定条件的文章列表
        List<Article> articles = queryArticles(condition);

        // 2.按发布人统计文章结果
        List<ArticleStatisticsByInUserResultDto> authors = articleStatisticsByInUser(articles, condition, null);

        // 3.按发布部门统计文章结果
        List<ArticleStatisticsByInDeptResultDto> depts = articleStatisticsByInDept(articles, condition);

        // 4.按发布人统计文章图表展示结果
        List<ArticleStatisticsByInUserChartResultDto> topAuthors = articleStatisticsTopByInUser(authors);

        // 5.各个结果添加到结果集
        ArticleMultiStatisticsResultDto result = new ArticleMultiStatisticsResultDto();
        result.setAuthors(authors);
        result.setDepts(depts);
        result.setTopAuthors(topAuthors);

        // 6.返回结果集
        return result;
    }

    /**
     * 查询指定条件的文章列表
     *
     * @param condition 查询条件Dto
     * @return 文章列表
     * @throws QCPromptException
     * @author QcCheng Tech
     * @date 2023/11/8
     */
    public List<Article> queryArticles(ArticleStatisticsConditionDto condition) throws QCPromptException {

        // 1.查询条件判空
        if (Objects.isNull(condition)) throw new QCPromptException("查询条件不能为空");
        if (StringUtils.isBlank(condition.getBegintm())) throw new QCPromptException("起始时间不能为空！");
        if (StringUtils.isBlank(condition.getEndtm())) throw new QCPromptException("截止时间不能为空！");

        // 2.解析起始时间和截至时间
        Date beginTime;
        Date endTime;
        try {
            beginTime = DateUtil.parseDate(condition.getBegintm());
        } catch (Exception e) {
            throw new QCPromptException("开始时间解析出错");
        }
        try {
            endTime = DateUtil.parseDate(condition.getEndtm());
        } catch (Exception e) {
            throw new QCPromptException("截至时间解析出错");
        }

        // 3.构造查询文章的条件
        LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>();
        // 3-1.查询列不查内容列和附件列
        wrapper.select(Article::getId, Article::getCid, Article::getTitle, Article::getCtype, Article::getKeywords,
                Article::getSource, Article::getAuthor, Article::getIntm, Article::getInuser, Article::getIndept,
                Article::getUptm, Article::getUpuser, Article::getFlag, Article::getTags, Article::getCount);
        // 3-2.查询状态-已发布
        wrapper.eq(Article::getFlag, CmsArticleStatusFlagEnum.PUBLISH);
        // 3-3.查询指定时间范围：查询时间段以日期为最小单位，后台自动处理截止时间往后1天，条件为：intm >= beignTime && intm < endTime
        wrapper.ge(Article::getIntm, beginTime).lt(Article::getIntm, DateUtil.addDays(endTime, 1));
        // 3-4.查询指定栏目，若无选择默认查询所有
        if (CollectionUtils.isNotEmpty(condition.getIds())) wrapper.in(Article::getCid, condition.getIds());
        // 3-5.排序：更新时间降序，添加时间降序
        wrapper.orderByDesc(Article::getUptm, Article::getIntm);
        // 3-6.指定作者查询 
        if (StringUtils.isNotBlank(condition.getAuthor())) {
            wrapper.eq(Article::getInuser, condition.getAuthor());
        }

        // 4.查询文章并返回
        return repository.selectList(wrapper);
    }

    /**
     * 按发布人统计文章结果
     * 统计结果按照发布人名称排序
     *
     * @param articles  需要统计的文章列表
     * @param condition 统计文章的查询条件
     * @param deptName  指定部门名称,如果有指定部门，则只统计该部门的发布人统计结果
     * @return 按发布人统计文章结果集
     * @throws QCPromptException
     * @author QcCheng Tech
     * @date 2023/11/8
     */
    public List<ArticleStatisticsByInUserResultDto> articleStatisticsByInUser(
            List<Article> articles, ArticleStatisticsConditionDto condition, String deptName) throws QCPromptException {

        // 1.按发布人统计文章结果集
        List<ArticleStatisticsByInUserResultDto> authors = new ArrayList<>();

        if (CollectionUtils.isNotEmpty(articles)) {
            for (Article article : articles) {
                // 2.如果发布人为空，则跳出本次循环
                if (StringUtils.isBlank(article.getInuser()))
                    continue;

                // 3.过滤结果集中是否已有当前作者记录
                ArticleStatisticsByInUserResultDto inUser = authors.stream()
                        .filter(author -> Objects.equals(author.getName(), article.getInuser()))
                        .findFirst().orElse(null);

                // 4.是否统计指定部门，如果是则只统计该指定部门的发布人统计结果；如果不是则默认统计所有
                if (StringUtils.isNotBlank(deptName)) {
                    // 4-1.如果当前部门为指定部门才统计，否则不统计
                    if (deptName.equals(article.getIndept())) {
                        // 4-2.如果结果集中没有当前作者记录，则添加当前作者记录到结果集；如果有记录则文章数量计数 + 1
                        if (Objects.isNull(inUser)) {
                            ArticleStatisticsByInUserResultDto currentInUser = new ArticleStatisticsByInUserResultDto();
                            currentInUser.setName(article.getInuser());
                            currentInUser.setCount(0x1);

                            // 4-2-1.查询当前作者的文章列表（每个作者的文章列表只在此处统计一次）
                            condition.setAuthor(article.getInuser());
                            List<Article> articleList = queryArticles(condition);
                            if (CollectionUtils.isNotEmpty(articleList)) {
                                List<ArticleDto> articleDtoList = ArticleMapper.MAPPER.toDtoList(articleList);
                                currentInUser.setArticles(articleDtoList);
                            }

                            //4-2-2.添加到结果集
                            authors.add(currentInUser);
                        } else {
                            inUser.setCount(inUser.getCount() + 0x1);
                        }
                    }
                } else {
                    // 4-3.如果结果集中没有当前作者记录，则添加当前作者记录到结果集；如果有记录则文章数量计数 + 1
                    if (Objects.isNull(inUser)) {
                        ArticleStatisticsByInUserResultDto currentInUser = new ArticleStatisticsByInUserResultDto();
                        currentInUser.setName(article.getInuser());
                        currentInUser.setCount(0x1);

                        // 4-3-1.查询当前作者的文章列表（每个作者的文章列表只在此处统计一次）
                        condition.setAuthor(article.getInuser());
                        List<Article> articleList = queryArticles(condition);
                        if (CollectionUtils.isNotEmpty(articleList)) {
                            List<ArticleDto> articleDtoList = ArticleMapper.MAPPER.toDtoList(articleList);
                            currentInUser.setArticles(articleDtoList);
                        }

                        // 4-3-2.添加到结果集
                        authors.add(currentInUser);
                    } else {
                        inUser.setCount(inUser.getCount() + 0x1);
                    }
                }

            }
        }

        // 5.统计结果按照发布人名称排序后返回（A-Z）
        List<ArticleStatisticsByInUserResultDto> sortResult = authors.stream()
                .sorted(Comparator.comparing(ArticleStatisticsByInUserResultDto::getName, new Comparator<String>() {
                    private Collator collator = Collator.getInstance(Locale.CHINA);

                    @Override
                    public int compare(String o1, String o2) {
                        return collator.compare(o1, o2);
                    }
                })).collect(Collectors.toList());

        return sortResult;
    }

    /**
     * 按发布部门统计文章结果
     *
     * @param articles  需要统计的文章列表
     * @param condition 统计文章的查询条件
     * @return 按发布部门统计文章结果集
     * @author QcCheng Tech
     * @date 2023/11/8
     */
    public List<ArticleStatisticsByInDeptResultDto> articleStatisticsByInDept(
            List<Article> articles, ArticleStatisticsConditionDto condition) throws QCPromptException {

        // 1.按部门统计文章结果集
        List<ArticleStatisticsByInDeptResultDto> depts = new ArrayList<>();

        if (CollectionUtils.isNotEmpty(articles)) {
            for (Article article : articles) {
                // 2.如果部门为空，则跳过本次循环
                if (StringUtils.isBlank(article.getIndept())) {
                    continue;
                }

                // 3.过滤结果集中是否已有当前部门记录
                ArticleStatisticsByInDeptResultDto inDept = depts.stream()
                        .filter(dept -> Objects.equals(dept.getName(), article.getIndept()))
                        .findFirst().orElse(null);

                // 3-1.如果没有记录则当前部门记录添加到结果集；如果有记录则文章数量计数 + 1
                if (Objects.isNull(inDept)) {
                    ArticleStatisticsByInDeptResultDto currentDept = new ArticleStatisticsByInDeptResultDto();
                    currentDept.setName(article.getIndept());
                    currentDept.setCount(0x1);

                    // 3-1-1.统计当前部门中按发布人统计文章结果集（每个部门中显示发文的发布人名称和文章数量，统计结果只在此处统计一次）
                    List<ArticleStatisticsByInUserResultDto> users = articleStatisticsByInUser(articles, condition, article.getIndept());
                    currentDept.setUsers(users);

                    // 3-1-2.添加到结果集
                    depts.add(currentDept);
                } else {
                    inDept.setCount(inDept.getCount() + 0x1);
                }
            }
        }

        // 4.统计结果按照发布部门名称排序后返回（A-Z）
        List<ArticleStatisticsByInDeptResultDto> sortResult = depts.stream()
                .sorted(Comparator.comparing(ArticleStatisticsByInDeptResultDto::getName, new Comparator<String>() {
                    private Collator collator = Collator.getInstance(Locale.CHINA);

                    @Override
                    public int compare(String o1, String o2) {
                        return collator.compare(o1, o2);
                    }
                })).collect(Collectors.toList());

        return sortResult;
    }

    /**
     * 按发布人统计前5条数据，剩下的统计为其他
     *
     * @param authors 按发布人统计文章结果集
     * @return 按发布人统计前n，剩下的合并为其他
     * @author QcCheng Tech
     * @date 2023/11/9
     */
    public List<ArticleStatisticsByInUserChartResultDto> articleStatisticsTopByInUser(List<ArticleStatisticsByInUserResultDto> authors) {
        // 1.按发布人统计文章结果集排序：按count降序
        List<ArticleStatisticsByInUserResultDto> authorsBySort = authors.stream()
                .sorted(Comparator.comparing(ArticleStatisticsByInUserResultDto::getCount).reversed())
                .collect(Collectors.toList());

        List<ArticleStatisticsByInUserChartResultDto> result = new ArrayList<>();

        if (authorsBySort.size() > 5) {
            // 2.排序后的结果集取前N条数据，默认取5条
            List<ArticleStatisticsByInUserResultDto> topAuthors = authorsBySort.subList(0, 5);

            // 3.取出剩下的数据集合（按发布人统计文章结果集 - 前N条结果集）
            List<ArticleStatisticsByInUserResultDto> otherAuthor = authorsBySort.stream()
                    .filter(author -> !topAuthors.contains(author)).collect(Collectors.toList());

            // 4.剩下的结果集合并为”其他“，统计”其他“的文章数量，将”其他“添加到前N条结果集中
            ArticleStatisticsByInUserResultDto author = new ArticleStatisticsByInUserResultDto();
            author.setName("其他");
            author.setCount(0x0);
            otherAuthor.stream().forEach(other -> {
                author.setCount(author.getCount() + other.getCount());
            });
            topAuthors.add(author);

            // 5.前N条结果集处理为需要返回的结果集
            topAuthors.stream().forEach(top -> {
                ArticleStatisticsByInUserChartResultDto dto = new ArticleStatisticsByInUserChartResultDto();
                dto.setName(top.getName());
                dto.setCount(top.getCount());
                result.add(dto);
            });
        } else {
            // 2.排序后的结果集处理为需要返回的结果集
            authorsBySort.stream().forEach(top -> {
                ArticleStatisticsByInUserChartResultDto dto = new ArticleStatisticsByInUserChartResultDto();
                dto.setName(top.getName());
                dto.setCount(top.getCount());
                result.add(dto);
            });
        }

        return result;
    }

    /**
     * 按发布人和发布部门统计文章，返回未合并处理的结果
     *
     * @param condition 查询条件
     * @return 未做合并处理的统计结果
     * @throws QCPromptException
     */
    public ArticleMultiStatisticsNotMergedRowResultDto statisticsByInUserAndDeptNotMergedRow(ArticleStatisticsConditionDto condition) throws QCPromptException {
 
        //已经处理的合并统计结果
        ArticleMultiStatisticsResultDto mergedResult = statisticsByInUserAndDept(condition);

        //将合并处理的发布人统计结果拆分为单条数据
        List<ArticleStatisticsByInUserResultDto> inusers = mergedResult.getAuthors();
        List<ArticleStatisticsByInUserNotMergedRowResultDto> inuserNotMered = new ArrayList<>();
        inusers.stream().forEach(inuser -> {
            List<ArticleStatisticsByInUserNotMergedRowResultDto> inuserResult = ArticleMapper.MAPPER.toNotMergedByInUserDtoList(inuser.getArticles());
            inuserResult.stream().forEach(article -> {
                article.setInUserCount(inuser.getCount());
                inuserNotMered.add(article);
            });

        });

        //将合并处理的发布部门统计结果拆分为单条数据
        List<ArticleStatisticsByInDeptResultDto> indepts = mergedResult.getDepts();
        List<ArticleStatisticsByInDeptNotMergedRowResultDto> indeptNotMered = new ArrayList<>();
        indepts.stream().forEach(dept -> {
            dept.getUsers().stream().forEach(user -> {
                List<ArticleStatisticsByInDeptNotMergedRowResultDto> userResult = ArticleMapper.MAPPER.toNotMergedByInDeptDtoList(user.getArticles());
                userResult.stream().forEach(article -> {
                    article.setInDeptCount(dept.getCount());
                    article.setInUserCount(user.getCount());
                    indeptNotMered.add(article);
                });
            });
        });

        //拼装返回结果对象
        ArticleMultiStatisticsNotMergedRowResultDto resultDto = new ArticleMultiStatisticsNotMergedRowResultDto();
        resultDto.setAuthors(inuserNotMered);
        resultDto.setDepts(indeptNotMered);
        resultDto.setTopAuthors(mergedResult.getTopAuthors());

        return resultDto;
    }


}
